
<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   
      <title>13.3.&nbsp;romantest.py 介绍</title>
      <link rel="stylesheet" href="../diveintopython.css" type="text/css">
      <link rev="made" href="mailto:f8dy@diveintopython.org">
      <meta name="generator" content="DocBook XSL Stylesheets V1.52.2">
      <meta name="keywords" content="Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free">
      <meta name="description" content="Python from novice to pro">
      <link rel="home" href="../toc/index.html" title="Dive Into Python">
      <link rel="up" href="index.html" title="第&nbsp;13&nbsp;章&nbsp;单元测试">
      <link rel="previous" href="diving_in.html" title="13.2.&nbsp;深入">
      <link rel="next" href="testing_for_success.html" title="13.4.&nbsp;正面测试 (Testing for success)">
   </head>
   <body>
      <table id="Header" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td id="breadcrumb" colspan="5" align="left" valign="top">导航：<a href="../index.html">起始页</a>&nbsp;&gt;&nbsp;<a href="../toc/index.html">Dive Into Python</a>&nbsp;&gt;&nbsp;<a href="index.html">单元测试</a>&nbsp;&gt;&nbsp;<span class="thispage">romantest.py 介绍</span></td>
            <td id="navigation" align="right" valign="top">&nbsp;&nbsp;&nbsp;<a href="diving_in.html" title="上一页: “深入”">&lt;&lt;</a>&nbsp;&nbsp;&nbsp;<a href="testing_for_success.html" title="下一页: “正面测试 (Testing for success)”">&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3" id="logocontainer">
               <h1 id="logo"><a href="../index.html" accesskey="1">深入 Python :Dive Into Python 中文版</a></h1>
               <p id="tagline">Python 从新手到专家 [Dip_5.4b_CPyUG_Release]</p>
            </td>
            <td colspan="3" align="right">
               <form id="search" method="GET" action="http://www.google.com/custom">
                  <p><label for="q" accesskey="4">Find:&nbsp;</label><input type="text" id="q" name="q" size="20" maxlength="255" value=""> <input type="submit" value="搜索"><input type="hidden" name="domains" value="woodpecker.org.cn/diveintopython"><input type="hidden" name="sitesearch" value="www.woodpecker.org.cn/diveintopython"></p>
               </form>
            </td>
         </tr>
      </table>
      <!--#include virtual="/inc/ads" -->
      <div class="section" lang="zh_cn">
         <div class="titlepage">
            <div>
               <div>
                  <h2 class="title"><a name="roman.romantest"></a>13.3.&nbsp;<tt class="filename">romantest.py</tt> 介绍
                  </h2>
               </div>
            </div>
            <div></div>
         </div>
         <div class="abstract">
            <p>这是将被开发并保存为 <tt class="filename">roman.py</tt> 的罗马数字转换程序的完整测试组件 (test suite)。很难立刻看出它们是如何协同工作的，似乎所有类或者方法之间都没有关系。这是有原因的，而且你很快就会明了。
            </p>
         </div>
         <div class="example"><a name="d0e32342"></a><h3 class="title">例&nbsp;13.1.&nbsp;<tt class="filename">romantest.py</tt></h3>
            <p>如果您还没有下载本书附带的样例程序, 可以 <a href="http://www.woodpecker.org.cn/diveintopython/download/diveintopython-exampleszh-cn-5.4b.zip" title="Download example scripts">下载本程序和其他样例程序</a>。
            </p><pre class="programlisting">
<span class='pystring'>"""Unit test for roman.py"""</span>

<span class='pykeyword'>import</span> roman
<span class='pykeyword'>import</span> unittest

<span class='pykeyword'>class</span><span class='pyclass'> KnownValues</span>(unittest.TestCase):                          
    knownValues = ( (1, <span class='pystring'>'I'</span>),
                    (2, <span class='pystring'>'II'</span>),
                    (3, <span class='pystring'>'III'</span>),
                    (4, <span class='pystring'>'IV'</span>),
                    (5, <span class='pystring'>'V'</span>),
                    (6, <span class='pystring'>'VI'</span>),
                    (7, <span class='pystring'>'VII'</span>),
                    (8, <span class='pystring'>'VIII'</span>),
                    (9, <span class='pystring'>'IX'</span>),
                    (10, <span class='pystring'>'X'</span>),
                    (50, <span class='pystring'>'L'</span>),
                    (100, <span class='pystring'>'C'</span>),
                    (500, <span class='pystring'>'D'</span>),
                    (1000, <span class='pystring'>'M'</span>),
                    (31, <span class='pystring'>'XXXI'</span>),
                    (148, <span class='pystring'>'CXLVIII'</span>),
                    (294, <span class='pystring'>'CCXCIV'</span>),
                    (312, <span class='pystring'>'CCCXII'</span>),
                    (421, <span class='pystring'>'CDXXI'</span>),
                    (528, <span class='pystring'>'DXXVIII'</span>),
                    (621, <span class='pystring'>'DCXXI'</span>),
                    (782, <span class='pystring'>'DCCLXXXII'</span>),
                    (870, <span class='pystring'>'DCCCLXX'</span>),
                    (941, <span class='pystring'>'CMXLI'</span>),
                    (1043, <span class='pystring'>'MXLIII'</span>),
                    (1110, <span class='pystring'>'MCX'</span>),
                    (1226, <span class='pystring'>'MCCXXVI'</span>),
                    (1301, <span class='pystring'>'MCCCI'</span>),
                    (1485, <span class='pystring'>'MCDLXXXV'</span>),
                    (1509, <span class='pystring'>'MDIX'</span>),
                    (1607, <span class='pystring'>'MDCVII'</span>),
                    (1754, <span class='pystring'>'MDCCLIV'</span>),
                    (1832, <span class='pystring'>'MDCCCXXXII'</span>),
                    (1993, <span class='pystring'>'MCMXCIII'</span>),
                    (2074, <span class='pystring'>'MMLXXIV'</span>),
                    (2152, <span class='pystring'>'MMCLII'</span>),
                    (2212, <span class='pystring'>'MMCCXII'</span>),
                    (2343, <span class='pystring'>'MMCCCXLIII'</span>),
                    (2499, <span class='pystring'>'MMCDXCIX'</span>),
                    (2574, <span class='pystring'>'MMDLXXIV'</span>),
                    (2646, <span class='pystring'>'MMDCXLVI'</span>),
                    (2723, <span class='pystring'>'MMDCCXXIII'</span>),
                    (2892, <span class='pystring'>'MMDCCCXCII'</span>),
                    (2975, <span class='pystring'>'MMCMLXXV'</span>),
                    (3051, <span class='pystring'>'MMMLI'</span>),
                    (3185, <span class='pystring'>'MMMCLXXXV'</span>),
                    (3250, <span class='pystring'>'MMMCCL'</span>),
                    (3313, <span class='pystring'>'MMMCCCXIII'</span>),
                    (3408, <span class='pystring'>'MMMCDVIII'</span>),
                    (3501, <span class='pystring'>'MMMDI'</span>),
                    (3610, <span class='pystring'>'MMMDCX'</span>),
                    (3743, <span class='pystring'>'MMMDCCXLIII'</span>),
                    (3844, <span class='pystring'>'MMMDCCCXLIV'</span>),
                    (3888, <span class='pystring'>'MMMDCCCLXXXVIII'</span>),
                    (3940, <span class='pystring'>'MMMCMXL'</span>),
                    (3999, <span class='pystring'>'MMMCMXCIX'</span>))                       

    <span class='pykeyword'>def</span><span class='pyclass'> testToRomanKnownValues</span>(self):                          
        <span class='pystring'>"""toRoman should give known result with known input"""</span>
        <span class='pykeyword'>for</span> integer, numeral <span class='pykeyword'>in</span> self.knownValues:              
            result = roman.toRoman(integer)                    
            self.assertEqual(numeral, result)                  

    <span class='pykeyword'>def</span><span class='pyclass'> testFromRomanKnownValues</span>(self):                          
        <span class='pystring'>"""fromRoman should give known result with known input"""</span>
        <span class='pykeyword'>for</span> integer, numeral <span class='pykeyword'>in</span> self.knownValues:                
            result = roman.fromRoman(numeral)                    
            self.assertEqual(integer, result)                    

<span class='pykeyword'>class</span><span class='pyclass'> ToRomanBadInput</span>(unittest.TestCase):                            
    <span class='pykeyword'>def</span><span class='pyclass'> testTooLarge</span>(self):                                          
        <span class='pystring'>"""toRoman should fail with large input"""</span>                   
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 4000)

    <span class='pykeyword'>def</span><span class='pyclass'> testZero</span>(self):                                              
        <span class='pystring'>"""toRoman should fail with 0 input"""</span>                       
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, 0)   

    <span class='pykeyword'>def</span><span class='pyclass'> testNegative</span>(self):                                          
        <span class='pystring'>"""toRoman should fail with negative input"""</span>                
        self.assertRaises(roman.OutOfRangeError, roman.toRoman, -1)  

    <span class='pykeyword'>def</span><span class='pyclass'> testNonInteger</span>(self):                                        
        <span class='pystring'>"""toRoman should fail with non-integer input"""</span>             
        self.assertRaises(roman.NotIntegerError, roman.toRoman, 0.5) 

<span class='pykeyword'>class</span><span class='pyclass'> FromRomanBadInput</span>(unittest.TestCase):                                      
    <span class='pykeyword'>def</span><span class='pyclass'> testTooManyRepeatedNumerals</span>(self):                                       
        <span class='pystring'>"""fromRoman should fail with too many repeated numerals"""</span>              
        <span class='pykeyword'>for</span> s <span class='pykeyword'>in</span> (<span class='pystring'>'MMMM'</span>, <span class='pystring'>'DD'</span>, <span class='pystring'>'CCCC'</span>, <span class='pystring'>'LL'</span>, <span class='pystring'>'XXXX'</span>, <span class='pystring'>'VV'</span>, <span class='pystring'>'IIII'</span>):             
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)

    <span class='pykeyword'>def</span><span class='pyclass'> testRepeatedPairs</span>(self):                                                 
        <span class='pystring'>"""fromRoman should fail with repeated pairs of numerals"""</span>              
        <span class='pykeyword'>for</span> s <span class='pykeyword'>in</span> (<span class='pystring'>'CMCM'</span>, <span class='pystring'>'CDCD'</span>, <span class='pystring'>'XCXC'</span>, <span class='pystring'>'XLXL'</span>, <span class='pystring'>'IXIX'</span>, <span class='pystring'>'IVIV'</span>):               
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)

    <span class='pykeyword'>def</span><span class='pyclass'> testMalformedAntecedent</span>(self):                                           
        <span class='pystring'>"""fromRoman should fail with malformed antecedents"""</span>                   
        <span class='pykeyword'>for</span> s <span class='pykeyword'>in</span> (<span class='pystring'>'IIMXCC'</span>, <span class='pystring'>'VX'</span>, <span class='pystring'>'DCM'</span>, <span class='pystring'>'CMM'</span>, <span class='pystring'>'IXIV'</span>,
                  <span class='pystring'>'MCMC'</span>, <span class='pystring'>'XCX'</span>, <span class='pystring'>'IVI'</span>, <span class='pystring'>'LM'</span>, <span class='pystring'>'LD'</span>, <span class='pystring'>'LC'</span>):                       
            self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, s)

<span class='pykeyword'>class</span><span class='pyclass'> SanityCheck</span>(unittest.TestCase):        
    <span class='pykeyword'>def</span><span class='pyclass'> testSanity</span>(self):                    
        <span class='pystring'>"""fromRoman(toRoman(n))==n for all n"""</span>
        <span class='pykeyword'>for</span> integer <span class='pykeyword'>in</span> range(1, 4000):       
            numeral = roman.toRoman(integer) 
            result = roman.fromRoman(numeral)
            self.assertEqual(integer, result)

<span class='pykeyword'>class</span><span class='pyclass'> CaseCheck</span>(unittest.TestCase):                   
    <span class='pykeyword'>def</span><span class='pyclass'> testToRomanCase</span>(self):                        
        <span class='pystring'>"""toRoman should always return uppercase"""</span>  
        <span class='pykeyword'>for</span> integer <span class='pykeyword'>in</span> range(1, 4000):                
            numeral = roman.toRoman(integer)          
            self.assertEqual(numeral, numeral.upper())

    <span class='pykeyword'>def</span><span class='pyclass'> testFromRomanCase</span>(self):                      
        <span class='pystring'>"""fromRoman should only accept uppercase input"""</span>
        <span class='pykeyword'>for</span> integer <span class='pykeyword'>in</span> range(1, 4000):                
            numeral = roman.toRoman(integer)          
            roman.fromRoman(numeral.upper())          
            self.assertRaises(roman.InvalidRomanNumeralError,
                              roman.fromRoman, numeral.lower())

<span class='pykeyword'>if</span> __name__ == <span class='pystring'>"__main__"</span>:
    unittest.main()   </pre></div>
         <div class="furtherreading">
            <h3>进一步阅读</h3>
            <ul>
               <li><a href="http://pyunit.sourceforge.net/"> <span class="application">PyUnit</span> 主页</a> 对于使用 <a href="http://pyunit.sourceforge.net/pyunit.html"><tt class="filename">unittest</tt> 框架</a> 以及本章没能涵盖的高级特性有深入的讨论。
               </li>
               <li><a href="http://pyunit.sourceforge.net/pyunit.html"> <span class="application">PyUnit</span> <span class="acronym">FAQ</span></a> 解释了 <a href="http://pyunit.sourceforge.net/pyunit.html#WHERE">为什么测试用例要和被测试代码分开存放</a> 。
               </li>
               <li><a href="http://www.python.org/doc/current/lib/"><i class="citetitle"><span class="application">Python</span> Library Reference</i></a> 总结了 <a href="http://www.python.org/doc/current/lib/module-unittest.html"><tt class="filename">unittest</tt></a> 模块。
               </li>
               <li><a href="http://www.extremeprogramming.org/">ExtremeProgramming.org</a> 讨论 <a href="http://www.extremeprogramming.org/rules/unittests.html">你为什么需要编写单元测试</a>。
               </li>
               <li><a href="http://www.c2.com/cgi/wiki">The Portland Pattern Repository</a> 有一个持续的 <a href="http://www.c2.com/cgi/wiki?UnitTests">单元测试</a> 讨论，包括了一个 <a href="http://www.c2.com/cgi/wiki?StandardDefinitionOfUnitTest">标准的定义</a>，为什么你需要 <a href="http://www.c2.com/cgi/wiki?CodeUnitTestFirst">首先开发单元测试代码</a> 以及另外一些深层次 <a href="http://www.c2.com/cgi/wiki?UnitTestTrial">案例</a>。
               </li>
            </ul>
         </div>
      </div>
      <table class="Footer" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td width="35%" align="left"><br><a class="NavigationArrow" href="diving_in.html">&lt;&lt;&nbsp;深入</a></td>
            <td width="30%" align="center"><br>&nbsp;<span class="divider">|</span>&nbsp;<a href="index.html#roman.intro" title="13.1.&nbsp;罗马数字程序介绍 II">1</a> <span class="divider">|</span> <a href="diving_in.html" title="13.2.&nbsp;深入">2</a> <span class="divider">|</span> <span class="thispage">3</span> <span class="divider">|</span> <a href="testing_for_success.html" title="13.4.&nbsp;正面测试 (Testing for success)">4</a> <span class="divider">|</span> <a href="testing_for_failure.html" title="13.5.&nbsp;负面测试 (Testing for failure)">5</a> <span class="divider">|</span> <a href="testing_for_sanity.html" title="13.6.&nbsp;完备性检测 (Testing for sanity)">6</a>&nbsp;<span class="divider">|</span>&nbsp;
            </td>
            <td width="35%" align="right"><br><a class="NavigationArrow" href="testing_for_success.html">正面测试 (Testing for success)&nbsp;&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3"><br></td>
         </tr>
      </table>
      <div class="Footer">
         <p class="copyright">Copyright © 2000, 2001, 2002, 2003, 2004 <a href="mailto:mark@diveintopython.org">Mark Pilgrim</a></p>
         <p class="copyright">Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007 <a href="mailto:python-cn@googlegroups.com">CPyUG (邮件列表)</a></p>
      </div>
   </body>
</html>